package com.jivam.lottery.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.jivam.lottery.main.Analyse;
import com.jivam.lottery.model.Expression;
import com.jivam.lottery.model.Formula;
import com.jivam.lottery.model.KeyFormula;
import com.jivam.lottery.model.Num;
import com.jivam.lottery.model.PeriodNum;
import com.jivam.lottery.panel.AbstractPanel;
import com.jivam.lottery.panel.FormulaPanel;
import com.jivam.lottery.util.Constants;
import com.jivam.lottery.util.ExpressionUtil;
import com.jivam.lottery.util.Utils;

/**
 * 数据处理核心类，当导入或者面板添加彩票数据时，对新数据的处理
 * @author Jivam
 */
public class DataCalculator {
	private static final List<AbstractPanel> panels = new ArrayList<AbstractPanel>();//面板集合
	
	public static List<List<Num>> numls;//统计期数中的数字
	public static LinkedList<PeriodNum> pns ;//历史期数
	private static List<KeyFormula> fdata;//公式数据
	private static Map<String,String> fmap;//彩票算码工具 和 统计公式的 映射关系
	private static Map<String,Integer> hns;//所有的历史期数

	private DataCalculator(){
	}
	
	static{
		reload();
	}
	
	/**
	 * 重新加载数据
	 */
	public static void reload(){
		pns = DataUtil.getData(Constants.KEY_PERIOD_NUM);
		fdata = DataUtil.getData(Constants.KEY_FORMULA);
		numls = DataUtil.getData(Constants.KEY_NUMS);
		fmap = DataUtil.getData(Constants.KEY_FORMULA_MAP);
		hns = DataUtil.getData(Constants.KEY_ALL_PERIOD_NUM);
	}
	
	/**
	 * 注册面板
	 * @param ob
	 */
	public static void registerOb(AbstractPanel ob) {
		panels.add(ob);
	}
	
	/**
	 * 批量导入列表
	 * @param nums
	 */
	public static void importPeriods(List<PeriodNum> nums) {
		for(PeriodNum num : nums){
			dealLottery(num);
		}
		DataUtil.addData(Constants.KEY_DATA_VERSION, Constants.DATA_VERSION);//更新数据版本
		save();
		fullUpdate();
		Analyse.getInstanse().reloadPanel();
	}
	
	/**
	 * 面板中添加期数
	 * @param num
	 */
	public static void addPeriod(PeriodNum num) {
		dealLottery(num);
		save();
		update();
		DataUtil.writePeriod(num.periodToString());
	}
	

	
	/**
	 * 处理单期彩票
	 * @param num
	 * @param pass 批量导入的时候，前面的期数不需要计算, true = 不计算
	 */
	private static void dealLottery(PeriodNum num) {
		dealPeriod(num);//期数面板
		dealNum(num);//期数累计
		dealCalculator(num);//计算统计
	}
	
	/**
	 * 保存数据
	 */
	private static void save(){
		sortList();
		DataUtil.saveData();
	}
	
	/**
	 * 更新所有注册的面板
	 */
	private static void update(){
		for(AbstractPanel ob : panels){
			ob.reloadPanel();
		}
	}
	
	/**
	 * 更新所有注册的面板，包括数据集
	 */
	private static void fullUpdate(){
		AbstractPanel ob = null;
		FormulaPanel fp = null;
		for(int i = 0,j = 0; i<panels.size();i++){
			ob = panels.get(i);
			if(ob instanceof FormulaPanel){
				fp = (FormulaPanel)ob;
				fp.reloadKeyFormula(fdata.get(j));
				j++;
			}
			ob.reloadPanel();
		}
	}

	/**
	 * 是否在历史期数里面出现
	 * @param sn
	 * @return
	 */
	public static boolean isAppearedInHistory(String sn){
		return hns.containsKey(sn);
	}

	/**
	 * 获取映射关系
	 * @param key
	 * @return
	 */
	public static String getMappingRelation(String key){
		return fmap.get(key);
	}

	/**
	 * 获得计算面板中某个值的推荐数的等级列表
	 * @param key 计算面板名
	 * @return
	 */
	public static String[] getDataLevel(String key){
		if(fmap.containsKey(key)){
			String flist = fmap.get(key);
			String[] farray = flist.split(Constants.KEY_FORMULA_MAP_RIGHT_FLAG);
			int[] darray,darrayCopy;
			HashMap<Integer,Integer> voteMap = new HashMap<Integer, Integer>();
			for(String s : farray){
				darray = getFormulaData(s);
				if(darray != null){
					darrayCopy = new int[darray.length-1];
					System.arraycopy(darray, 1, darrayCopy, 0, darrayCopy.length);
					vote(getPhaseOneAppearResult(appear(darrayCopy)),voteMap);
				}
			}
			return getPhaseTwoVoteResult(voteMap, farray.length);
		}
		return null;
	}
	
	/**
	 * 获得最终的推荐数
	 * @param voteMap
	 * @param deep
	 * @return
	 */
	private static String[] getPhaseTwoVoteResult(Map<Integer,Integer> voteMap, int deep){
		voteMap = Utils.sortByValueAsc(voteMap);
		String[] result = new String[deep];
		Iterator<Integer> it = voteMap.keySet().iterator();
		Integer key,value;
		while(it.hasNext()){
			key = it.next();
			value = voteMap.get(key);
			if(result[value] == null){
				result[value] = "";
			}
			result[value] = result[value] + key;
		}
		return result;
	}
	
	/**
	 * 按照出现顺序的先后，选出推荐数
	 * @param appearSet
	 * @return
	 */
	private static int[] getPhaseOneAppearResult(Set<Integer> appearSet){
		Iterator<Integer> it = appearSet.iterator();
		int[] tmp = new int[appearSet.size()];
		int i = 0;
		while(i < tmp.length){
			tmp[i] = it.next();
			i++;
		}
		return tmp;
	}
	
	/**
	 * 按照出现顺序获得不重复Set
	 * @param nums
	 * @return
	 */
	private static Set<Integer> appear(int[] nums){
		LinkedHashSet<Integer> appearSet = new LinkedHashSet<Integer>(7);
		for(int num : nums){
			appearSet.add(num);
			if(appearSet.size() == Constants.CALCULATOR_FORMULA_SIZE){
				break;
			}
		}
		return appearSet;
	}
	
	/**
	 * 通过出现次数由高到低选出推举数，目前不使用这个方法，使用请改private
	 * @param voteMap
	 * @return
	 */
	protected static int[] getPhaseOneVoteResult(Map<Integer,Integer> voteMap){
		int length = voteMap.size();
		if(length > Constants.CALCULATOR_FORMULA_SIZE){
			voteMap = Utils.sortByValueDesc(voteMap);
			length = Constants.CALCULATOR_FORMULA_SIZE;
		}
		int[] tmp = new int[length];
		Iterator<Integer> it = voteMap.keySet().iterator();
		int i = 0;
		while(i < length){
			tmp[i] = it.next();
			i++;
		}
		return tmp;
	}
	
	/**
	 * 投票方式获得Map
	 * @param nums 所有票集合
	 * @return
	 */
	protected static Map<Integer, Integer> vote(int[] nums){
		HashMap<Integer,Integer> voteMap = new HashMap<Integer, Integer>();
		vote(nums, voteMap);
		return voteMap;
	}
	
	/**
	 * 循环数组投票
	 * @param nums 票集合
	 * @param voteMap 投票箱
	 */
	private static void vote(int[] nums, final Map<Integer,Integer> voteMap){
		for(int num : nums){
			vote(num, voteMap);
		}
	}
	
	/**
	 * 给对应的数字投票
	 * @param num 数字
	 * @param voteMap 投票箱
	 */
	private static void vote(int num, final Map<Integer,Integer> voteMap){
		if(voteMap.containsKey(num)){
			voteMap.put(num, voteMap.get(num)+1);
		}else{
			voteMap.put(num, 0);
		}
	}
	
	/**
	 * 获取某个公式的最新数据集
	 * @param key
	 * @return
	 */
	private static int[] getFormulaData(String key){
		for(KeyFormula f : fdata){
			if(key.equals(f.getKey())){
				return f.getLastData();
			}
		}
		return null;
	}
	
	/**
	 * 添加累计的期数
	 * @param num
	 */
	private static void dealPeriod(PeriodNum num) {
		hns.put(num.toString(), num.getId());
		if(pns.size() == Constants.MAX_PERIOD){
			pns.removeFirst();
		}
		pns.addLast(num);
	}
	
	/**
	 * 对期数中的各位进行排序
	 */
	private static void sortList(){
		for(List<Num> nums : numls){
			Collections.sort(nums);
		}
	}
	
	/**
	 * 期数统计更新
	 * @param num
	 */
	private static void dealNum(PeriodNum pn) {
		int theNum = 0;
		for(List<Num> nums : numls){
			theNum = pn.getKeyResult(nums.get(0).getPosition());
			for(Num num : nums){
				num.addPeriod(theNum);
			}
		}
	}
	
	
	/**
	 * 所有公式计算
	 * @param num
	 */
	private static void dealCalculator(PeriodNum num) {
		if(pns.size() < Constants.MAX_PERIOD || fdata == null)
			return ;
		Map<String,Integer> dict = getDict();//数字字典
		Map<String,Integer> numDict = getDict(num);
		List<Formula> flist = null;//对应面板的公式列表
		Formula f = null;//公式
		int[] data = null;//新数据
		int[] lastData = null;//老数据
		for(KeyFormula kf : fdata){//遍历面板公式集
			flist = kf.getFormulas();
			lastData = kf.getLastData();
			data = new int[flist.size()+1];
			data[0] = num.getId()+1;
			for(int i = 0;i<flist.size();i++){//遍历每个公式
				f = flist.get(i);
				data[i+1] = ExpressionUtil.getValueByPostfixForLottery(f.getPostfixexp(), dict);
				if(lastData != null){
					f.addValue(isGuessRight(lastData[i+1], numDict, kf.getExpressions()));
				}
			}
			kf.addDatas(data);
		}
	}
	
	/**
	 * 查看公式计算结果和公式左边是否正确
	 * @param value 公式右边的计算结果
	 * @param dict 字典
	 * @param list 公式左边列表
	 * @return
	 */
	public static boolean isGuessRight(int value, Map<String,Integer> dict, List<Expression> list){
		if(list == null || list.isEmpty()){
			return false;
		}
		List<Integer> results = new ArrayList<Integer>();
		for(Expression e : list){
			results.add(ExpressionUtil.getValueByPostfixForLottery(e.getPostfixexp(), dict));
		}
		return results.contains(value);
	}
	
	/**
	 * 获取公式面板列表
	 * @return
	 */
	public static List<KeyFormula> getFdata() {
		return fdata;
	}

	/**
	 * 获得期数列表字典，用于公式右边
	 * @return
	 */
	public static Map<String,Integer> getDict(){
		Map<String,Integer> dict = new LinkedHashMap<String,Integer>();
		PeriodNum pn = null;
		Iterator<PeriodNum> it = pns.iterator();
		int i = 0;
		while(it.hasNext()){
			pn = it.next();
			addPeriodNumInDict(dict, pn, (Constants.MAX_PERIOD - i)+"");
			i++;
		}
		return dict;
	}
	
	/**
	 * 获得一期期数的字段，用于公式左边
	 * @param pn
	 * @return
	 */
	public static Map<String,Integer> getDict(PeriodNum pn){
		Map<String,Integer> dict = new LinkedHashMap<String,Integer>();
		addPeriodNumInDict(dict, pn, null);
		return dict;
	}
	
	private static void addPeriodNumInDict(Map<String,Integer> dict,PeriodNum pn,String flag){
		if(dict == null || pn == null){
			return;
		}
		if(flag == null){
			flag = "";
		}
		dict.put(Constants.FORMULA_KD + flag, pn.getSpan4());//跨度值
		dict.put(Constants.FORMULA_HE + flag, pn.getSum4());//和值
		dict.put(Constants.FORMULA_A + flag, pn.getBit(Constants.POSTION_A));
		dict.put(Constants.FORMULA_B + flag, pn.getBit(Constants.POSTION_B));
		dict.put(Constants.FORMULA_C + flag, pn.getBit(Constants.POSTION_C));
		dict.put(Constants.FORMULA_D + flag, pn.getBit(Constants.POSTION_D));
		dict.put(Constants.FORMULA_E + flag, pn.getBit(Constants.POSTION_E));
		dict.put(Constants.FORMULA_F + flag, pn.getBit(Constants.POSTION_F));
		dict.put(Constants.FORMULA_G + flag, pn.getBit(Constants.POSTION_G));
	}

	
	/**
	 * 获得指定期数的数字
	 * @param id
	 * @return
	 */
	public static PeriodNum getPeriod(int id){
		Iterator<PeriodNum> it = pns.iterator();
		PeriodNum pn = null;
		while (it.hasNext()) {
			pn = it.next();
			if(pn.getId() == id){
				return pn;
			}
		}
		return null;
	}
	
	/**
	 * 获取最新的一期数字
	 * @return
	 */
	public static int getNextPid(){
		PeriodNum pn = pns.getLast();
		return pn.getId()+1;
	}
	

}
